package com.forester.foresterplatform.system.advice;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.forester.foresterplatform.system.annotation.SystemLogging;
import com.forester.foresterplatform.system.enums.SystemLoggingLevel;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * 日志切面
 */
@Aspect
@Component
public class SystemLoggingAspect {
    /**
     * 返回通知: 记录日志内容为 方法名称、方法参数和返回值
     * @param joinPoint 连接点
     * @param systemLogging 标记连接点的自定义注解
     * @param result 被代理方法成功执行后的返回值
     */
    @AfterReturning(pointcut = "@annotation(systemLogging)", returning = "result")
    public void afterReturning(JoinPoint joinPoint, SystemLogging systemLogging, Object result) {
        // 获取到 logger
        Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());

        // 获取到方法名
        String methodName = joinPoint.getSignature().getName();

        // 获取到方法参数
        Object[] args = joinPoint.getArgs();

        // 获取日志级别
        SystemLoggingLevel level = systemLogging.level();

        // 拼接日志内容
        String s = buildLogMessage(systemLogging, result, methodName, args);

        // 使用 logger 输出
        switch (level) {
            case TRACE : logger.trace(s); break;
            case DEBUG : logger.debug(s); break;
            case INFO : logger.info(s); break;
            case WARN : logger.warn(s); break;
            case ERROR : logger.error(s); break;
        }
    }

    @AfterThrowing(pointcut = "@annotation(systemLogging)", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, SystemLogging systemLogging, Exception e) {
        // 获取到 logger
        Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());

        // 获取到方法名
        String methodName = joinPoint.getSignature().getName();

        // 获取到方法参数
        Object[] args = joinPoint.getArgs();

        // 获取日志级别
        SystemLoggingLevel level = systemLogging.level();

        // 拼接日志内容
        String s = buildLogMessage(systemLogging, e, methodName, args);

        // 使用 logger 输出
        switch (level) {
            case TRACE : logger.trace(s); break;
            case DEBUG : logger.debug(s); break;
            case INFO : logger.info(s); break;
            case WARN : logger.warn(s); break;
            case ERROR : logger.error(s); break;
        }
    }

    /**
     * 构建返回通知需要的日志字符串
     * @param systemLogging
     * @param result
     * @param methodName
     * @param args
     * @return
     */
    private static String buildLogMessage(SystemLogging systemLogging, Object result, String methodName, Object[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append("调用方法: ").append(methodName);
        if(systemLogging.logParams()) {
            // 将参数转换为JSON字符串
            String argJson = JSON.toJSONString(args);
            sb.append(", 参数: ").append(argJson);
        }
        if(systemLogging.logResult()) {
            String resultJson = JSON.toJSONString(result);
            sb.append(", 返回值: ").append(resultJson);
        }
        return sb.toString();
    }

    /**
     * 构建异常通知需要的日志字符串
     * @param systemLogging
     * @param e
     * @param methodName
     * @param args
     * @return
     */
    private static String buildLogMessage(SystemLogging systemLogging, Exception e, String methodName, Object[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append("调用方法: ").append(methodName);
        if(systemLogging.logParams()) {
            // 将参数转换为JSON字符串
            String argJson = JSON.toJSONString(args);
            sb.append(", 参数: ").append(argJson);
        }
        if(systemLogging.logException()) {
            sb.append(", 异常信息: ").append(e.getMessage());
        }
        return sb.toString();
    }

}
